package vip.liux.backend.application.serviceImpl.coupon;

import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import vip.liux.application.dots.ListResultDto;
import vip.liux.application.dots.PagedResultDto;
import vip.liux.backend.application.service.coupon.*;
import vip.liux.backend.application.service.customer.CustomerDto;
import vip.liux.backend.application.service.customer.CustomerService;
import vip.liux.backend.infrastructure.code.BizCodeGenerator;
import vip.liux.backend.infrastructure.utils.ListJoinKit;
import vip.liux.contracts.models.coupon.Coupon;
import vip.liux.contracts.models.coupon.UserCoupon;
import vip.liux.contracts.models.coupon.UserCouponDomainService;
import vip.liux.contracts.repositories.coupon.UserCouponRepository;
import vip.liux.contracts.shared.enums.coupon.UsedTag;
import vip.liux.lock.LockTemplate;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static vip.liux.application.PageKit.toPage;
import static vip.liux.application.PageKit.toPaged;

/**
 * 用户优惠券服务实现类
 * 负责处理与用户优惠券相关的应用层逻辑
 */
@Service
public class UserCouponServiceImpl implements UserCouponService {
    private static final Logger log = LoggerFactory.getLogger(UserCouponServiceImpl.class);

    private final UserCouponDomainService userCouponDomainService;
    private final CouponService couponService;
    private final UserCouponRepository userCouponRepository;
    private final LockTemplate lockTemplate;
    private final BizCodeGenerator codeGenerator;
    private final CustomerService customerService;

    /**
     * 构造方法，注入依赖
     *
     * @param userCouponDomainService 用户优惠券领域服务
     * @param couponService           优惠券服务
     * @param userCouponRepository    用户优惠券仓储
     * @param lockTemplate            分布式锁模板
     * @param codeGenerator           编码生成器
     */
    public UserCouponServiceImpl(
            UserCouponDomainService userCouponDomainService,
            CouponService couponService,
            UserCouponRepository userCouponRepository,
            LockTemplate lockTemplate,
            BizCodeGenerator codeGenerator, CustomerService customerService
    ) {
        this.userCouponDomainService = userCouponDomainService;
        this.couponService = couponService;
        this.userCouponRepository = userCouponRepository;
        this.lockTemplate = lockTemplate;
        this.codeGenerator = codeGenerator;
        this.customerService = customerService;
    }

    /**
     * 领取优惠券
     * 校验优惠券是否可领取，并通过分布式锁确保领取操作的原子性
     *
     * @param couponNo 优惠券编号
     * @param userId   用户ID
     */
    @Override
    public void receiveCoupon(String couponNo, Long userId) {
        // 查询优惠券并校验领取条件
        Coupon coupon = userCouponDomainService.findCouponByNo(couponNo);
        userCouponDomainService.checkCoupon(coupon, userId);

        try {
            // 使用分布式锁执行领取操作，防止并发超发
            lockTemplate.execute("receive_coupon:" + couponNo, key -> {
                userCouponDomainService.checkCoupon(coupon, userId);
                String code = codeGenerator.generateUserCouponCode();
                UserCoupon userCoupon = userCouponDomainService.createUserCoupon(coupon, userId, code);
                return null;
            });
        } catch (Exception e) {
            log.error("领取优惠券失败", e);
            throw new IllegalArgumentException("领取优惠券失败");
        }
    }

    /**
     * 查询用户优惠券
     * 先自动更新过期状态，再根据用户ID和使用状态查询
     *
     * @param userId 用户ID
     * @param type   优惠券使用状态
     * @return 用户优惠券Dto列表
     */
    @Override
    public List<UserCouponDto> query(Long userId, UsedTag type) {
        LocalDateTime now = LocalDateTime.now();
        // 更新过期优惠券状态
        int updated = userCouponRepository.updateExpiredCoupons(userId, UsedTag.UNUSED, now);
        if (updated > 0) {
            log.debug("更新过期优惠券成功, userId: {}, updatedCount: {}", userId, updated);
        }
        // 查询指定状态的优惠券
        List<UserCoupon> coupons = userCouponRepository.findByUserIdAndUsedTag(userId, type);
        return coupons.stream()
                .map(uc -> AutoMapperProfile.INSTANCE.toUserCoupon(uc, uc.getCoupon()))
                .toList();
    }


    /**
     * 查询用户优惠券详情
     * 自动更新过期状态，返回指定ID和券码的优惠券详情
     *
     * @param id   用户优惠券ID
     * @param code 券码
     * @return 用户优惠券Dto
     */
    @Override
    public UserCouponDto detail(Long id, String code) {
        updateExpiredCouponsById(List.of(id));
        // 查询详情
        return userCouponRepository.findByIdAndCode(id, code)
                .map(uc -> AutoMapperProfile.INSTANCE.toUserCoupon(uc, uc.getCoupon()))
                .orElseThrow(() -> new IllegalArgumentException("优惠券不存在"));
    }

    /**
     * 查询可用优惠券并标记是否已领取
     *
     * @param userId 用户ID
     * @return 可用优惠券Dto列表
     */
    @Override
    public List<ShopCouponDto> queryAvailableCoupons(Long userId) {
        // 查询所有可用优惠券
        List<ShopCouponDto> coupons = couponService.queryAvailableCoupons();
        // 查询用户已领取的优惠券
        List<UserCoupon> userCoupons = userCouponRepository.findByUserId(userId);
        // 按优惠券编号分组统计已领取数量
        Map<String, Long> receivedCountMap = userCoupons.stream()
                .collect(Collectors.groupingBy(uc -> uc.getCoupon().getCouponNo(), Collectors.counting()));
        // 标记每个优惠券是否已达领取上限
        coupons.forEach(c -> {
            long receivedCount = receivedCountMap.getOrDefault(c.getCouponNo(), 0L);
            c.setReceived(NumberUtils.compare(c.getReceiveNumLimit(), (int) receivedCount) <= 0);
        });
        return coupons;
    }

    @Override
    public PagedResultDto<UserCouponDto> receivedList(QueryUserCouponDto input) {
        Page<UserCouponDto> page = userCouponRepository.getUserCoupons(
                        input.getCouponNo(),
                        input.getCouponName(),
                        input.getUserId(),
                        input.getUsedTag(),
                        input.getStart(),
                        input.getEnd(),
                        toPage(input))
                .map(uc -> AutoMapperProfile.INSTANCE.toUserCoupon(uc, uc.getCoupon()));
        if (page.isEmpty()) {
            return new PagedResultDto<>();
        }

        List<UserCouponDto> list = page.getContent();
        ListResultDto<CustomerDto> customers = customerService.findByUserIds(list.stream().map(UserCouponDto::getUserId).toList());

        ListJoinKit.backFill(
                list,
                customers.getItems(),
                UserCouponDto::getUserId,
                CustomerDto::getId,
                UserCouponDto::setUser
        );
        updateExpiredCouponsById(page.getContent().stream().map(UserCouponDto::getId).toList());
        return toPaged(page);
    }

    /**
     * 尝试更新过期优惠券状态
     * 根据ID列表更新过期优惠券的使用状态为未使用
     *
     * @param ids 用户优惠券ID列表
     */
    private void updateExpiredCouponsById(List<Long> ids) {
        int updated = userCouponRepository.updateExpiredCouponsById(
                ids, UsedTag.UNUSED, LocalDateTime.now());
        if (updated > 0) {
            log.debug("更新过期优惠券成功, id: {}, updatedCount: {}", ids, updated);
        }
    }
}